#include "QueryParser.h"

#include <algorithm>

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>

#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/home/phoenix/object/construct.hpp>

using boost::phoenix::construct;
using boost::phoenix::at_c;
using boost::phoenix::push_back;

using boost::spirit::qi::_1;
using boost::spirit::qi::eps;
using boost::spirit::qi::lit;
using boost::spirit::qi::rule;
using boost::spirit::qi::hold;
using boost::spirit::qi::attr;
using boost::spirit::qi::_val;
using boost::spirit::qi::int_;
using boost::spirit::qi::char_;
using boost::spirit::qi::digit;
using boost::spirit::qi::alpha;
using boost::spirit::qi::lexeme;
using boost::spirit::qi::double_;
using boost::spirit::qi::grammar;
using boost::spirit::qi::phrase_parse;

using boost::spirit::ascii::space;
using boost::spirit::ascii::string;
using boost::spirit::ascii::no_case;
using boost::spirit::ascii::space_type;


struct pql_declaration
{
	EntityType type;
	std::vector<std::string> names;
};

// for hold[]
void swap( query_parameter& p1, query_parameter& p2 )
{
	query_parameter temp;
	temp.type = p1.type;
	temp.value = p1.value;

	p1.type = p2.type;
	p1.value = p2.value;

	p2.type = temp.type;
	p2.value = temp.value;
}

// for hold[]
void swap( query_relation& r1, query_relation& r2 )
{
	query_relation temp;
	temp.type = r1.type;
	temp.parameters = r1.parameters;

	r1.type = r2.type;
	r1.parameters = r2.parameters;

	r2.type = temp.type;
	r2.parameters = temp.parameters;
}

struct pql_select
{
	std::vector<query_parameter> targets;
	std::vector<std::vector<query_relation>> relations;
};

struct pql_query
{
	std::vector<pql_declaration> variables;
	pql_select select;
};

BOOST_FUSION_ADAPT_STRUCT
(
	pql_declaration,
	(EntityType, type)
	(std::vector<std::string>, names)
)

BOOST_FUSION_ADAPT_STRUCT
(
	query_parameter,
	(std::string, value)
	(ParameterType, type)
)

BOOST_FUSION_ADAPT_STRUCT
(
	query_relation,
	(RelationType, type)
	(std::vector<query_parameter>, parameters)
)

BOOST_FUSION_ADAPT_STRUCT
(
	pql_select,
	(std::vector<query_parameter>, targets)
	(std::vector<std::vector<query_relation>>, relations)
)

BOOST_FUSION_ADAPT_STRUCT
(
	pql_query,
	(std::vector<pql_declaration>, variables)
	(pql_select, select)
)

template <typename Iterator>
struct pql_grammar : grammar<Iterator, pql_query(), space_type>
{
	rule<Iterator, std::string()> INTEGER;
	rule<Iterator, query_parameter()> any;
	rule<Iterator, query_parameter()> line;
	rule<Iterator, query_parameter()> name;
	rule<Iterator, query_parameter()> entity;
	rule<Iterator, query_parameter()> boolean;

	// Auxiliary grammar rules
	rule<Iterator, std::vector<query_parameter>(), space_type> tuple;
	rule<Iterator, query_parameter(), space_type> elem;
	rule<Iterator, std::string()> synonym;
	rule<Iterator, std::string()> attrName;
	rule<Iterator, query_parameter()> procRef;
	rule<Iterator, query_parameter()> varRef;
	rule<Iterator, query_parameter()> entRef;
	rule<Iterator, query_parameter()> stmtRef;
	rule<Iterator, query_parameter()> lineRef;
	rule<Iterator, query_parameter()> nodeRef;
	rule<Iterator, EntityType()> design_entity;
	
	
	// Grammar rules for select clause
	rule<Iterator, pql_select(), space_type> select_cl;
	rule<Iterator, pql_declaration(), space_type> declaration;
	rule<Iterator, std::vector<query_parameter>(), space_type> result_cl;
	
	rule<Iterator, std::vector<query_relation>(), space_type> with_cl;
	rule<Iterator, std::vector<query_relation>(), space_type> pattern_cl;
	rule<Iterator, std::vector<query_relation>(), space_type> suchthat_cl;

	rule<Iterator, std::vector<query_relation>(), space_type> attrCond;
	rule<Iterator, query_relation(), space_type> attrCompare;
	rule<Iterator, query_parameter()> attrRef;
	rule<Iterator, query_parameter()> ref;

	rule<Iterator, std::vector<query_relation>(), space_type> relCond;
	rule<Iterator, query_relation(), space_type> relRef;
	rule<Iterator, query_relation(), space_type> Modifies;
	rule<Iterator, query_relation(), space_type> ModifiesP;
	rule<Iterator, query_relation(), space_type> ModifiesS;
	rule<Iterator, query_relation(), space_type> Uses;
	rule<Iterator, query_relation(), space_type> UsesP;
	rule<Iterator, query_relation(), space_type> UsesS;
	rule<Iterator, query_relation(), space_type> Calls;
	rule<Iterator, query_relation(), space_type> CallsT;
	rule<Iterator, query_relation(), space_type> Parent;
	rule<Iterator, query_relation(), space_type> ParentT;
	rule<Iterator, query_relation(), space_type> Follows;
	rule<Iterator, query_relation(), space_type> FollowsT;
	rule<Iterator, query_relation(), space_type> Next;
	rule<Iterator, query_relation(), space_type> NextT;
	rule<Iterator, query_relation(), space_type> NextBip;
	rule<Iterator, query_relation(), space_type> NextBipT;
	rule<Iterator, query_relation(), space_type> Affects;
	rule<Iterator, query_relation(), space_type> AffectsT;
	rule<Iterator, query_relation(), space_type> AffectsBip;
	rule<Iterator, query_relation(), space_type> AffectsBipT;
	rule<Iterator, query_relation(), space_type> Sibling;

	rule<Iterator, std::vector<query_relation>(), space_type> patternCond;
	rule<Iterator, query_relation(), space_type> pattern;
	rule<Iterator, query_relation(), space_type> assign;
	rule<Iterator, query_parameter(), space_type> expression_spec;
	rule<Iterator, std::string(), space_type> expr;
	rule<Iterator, std::string(), space_type> term;
	rule<Iterator, std::string(), space_type> factor;

	rule<Iterator, query_relation(), space_type> if_;
	rule<Iterator, query_relation(), space_type> while_;


	rule<Iterator, pql_query(), space_type> start;

	pql_grammar() : pql_grammar::base_type( start )
	{
		INTEGER %= lexeme[ ( digit - "0" ) >> *digit ];
		any %= char_( '_' ) >> attr( ANY );
		line %= INTEGER >> attr( LINE );
		name %= lexeme[ '"' >> synonym >> '"' ] >> attr( NAME );
		entity %= synonym >> attr( ENTITY );
		boolean %= string( "BOOLEAN" ) >> attr( BOOL );

		// Auxiliary grammar rules
		tuple %= elem | ( '<' >> elem >> *( ',' >> elem ) >> '>' );
		elem %= hold[ attrRef ] | entity;
		synonym %= lexeme[ alpha >> *( alpha | digit | char_( '#' ) ) ];
		attrName %= string( "procName" ) | string( "varName" ) | string( "value" ) | string( "stmt#" );
		procRef %= entity | any | name;
		varRef %= entity | any | name;
		entRef %= entity | any | name | line ;
		stmtRef %= entity | any | line;
		lineRef %= entity | any | line;
		nodeRef %= entity | line;
		design_entity %= lit( "procedure" ) >> attr( kPROCEDURE ) | lit( "stmtLst" ) >> attr( kSTMTLST ) | lit( "stmt" ) >> attr( kSTMT ) | lit( "assign" ) >> attr( kASSIGN ) | lit( "call" ) >> attr( kCALL ) | lit( "while" ) >> attr( kWHILE ) | lit( "if" ) >> attr( kIF ) | lit( "variable" ) >> attr( kVARIABLE ) | lit( "constant" ) >> attr( kCONSTANT ) | lit( "prog_line" ) >> attr( kPROG_LINE );

		// Grammar rules for select clause
		select_cl %= no_case[ lit( "Select" ) ] >> result_cl [ at_c<0>( _val ) = _1 ] >> *( with_cl | pattern_cl | suchthat_cl ) >> -char_( ';' );
		declaration %= design_entity >> synonym >> *( ',' >> synonym [ push_back( at_c<1>( _val ), _1 ) ] )  >> ';';
		result_cl %= hold[ boolean [ push_back( _val, _1 ) ] ] | tuple;
		
		with_cl %= no_case[ lit( "with" ) ] >> attrCond;
		suchthat_cl %= no_case[ lit( "such" ) ] >> no_case[ lit( "that" ) ] >> relCond;
		pattern_cl %= no_case[ lit( "pattern" ) ] >> patternCond;
		
		attrCond %= attrCompare >> *( lit( "and" ) >> attrCompare );
		attrCompare %= attr( WITH ) >> attrRef >> '=' >> ref [ push_back( at_c<1>( _val ), _1 ) ];
		attrRef %= lexeme[ synonym >> char_( '.' ) >> attrName ] >> attr( ATTRIBUTE );
		ref %= name | line | attrRef;
		
		relCond %= relRef >> *( lit( "and" ) >> relRef );
		relRef %=  hold[ ModifiesS ] | hold[ ModifiesP ] | Modifies | hold[ UsesS ] | hold[ UsesP ] | Uses | CallsT | Calls | ParentT | Parent | FollowsT | Follows | NextBipT | NextBip | NextT | Next | AffectsBipT | AffectsBip | AffectsT | Affects | Sibling;
		ModifiesP %= no_case[ lit( "Modifies" ) ] >> attr( MODIFIES_P ) >> '(' >> name >> ',' >> varRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';
		ModifiesS %= no_case[ lit( "Modifies" ) ] >> attr( MODIFIES_S ) >> '(' >> line >> ',' >> varRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';
		Modifies %= no_case[ lit( "Modifies" ) ] >> attr( MODIFIES ) >> '(' >> entity >> ',' >> varRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';

		UsesP %= no_case[ lit( "Uses" ) ] >> attr( USES_P ) >> '(' >> name >> ',' >> varRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';
		UsesS %= no_case[ lit( "Uses" ) ] >> attr( USES_S ) >> '(' >> line >> ',' >> varRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';
		Uses %= no_case[ lit( "Uses" ) ] >> attr( USES ) >> '(' >> entity >> ',' >> varRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';

		Calls %= no_case[ lit( "Calls" ) ] >> attr( CALLS ) >> '(' >> procRef >> ',' >> procRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';
		CallsT %= no_case[ lit( "Calls*" ) ] >> attr( CALLS_T ) >> '(' >> procRef >> ',' >> procRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';

		Parent %= no_case[ lit( "Parent" ) ] >> attr( PARENT ) >> '(' >> stmtRef >> ',' >> stmtRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';
		ParentT %= no_case[ lit( "Parent*" ) ] >> attr( PARENT_T ) >> '(' >> stmtRef >> ',' >> stmtRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';

		Follows %= no_case[ lit( "Follows" ) ] >> attr( FOLLOWS ) >> '(' >> stmtRef >> ',' >> stmtRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';
		FollowsT %= no_case[ lit( "Follows*" ) ] >> attr( FOLLOWS_T ) >> '(' >> stmtRef >> ',' >> stmtRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';

		Next %= no_case[ lit( "Next" ) ] >> attr( NEXT ) >> '(' >> lineRef >> ',' >> lineRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';
		NextT %= no_case[ lit( "Next*" ) ] >> attr( NEXT_T ) >> '(' >> lineRef >> ',' >> lineRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';
		NextBip %= no_case[ lit( "NextBip" ) ] >> attr( NEXTBIP ) >> '(' >> lineRef >> ',' >> lineRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';
		NextBipT %= no_case[ lit( "NextBip*" ) ] >> attr( NEXTBIP_T ) >> '(' >> lineRef >> ',' >> lineRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';

		Affects %= no_case[ lit( "Affects" ) ] >> attr( AFFECTS ) >> '(' >> lineRef >> ',' >> lineRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';
		AffectsT %= no_case[ lit( "Affects*" ) ] >> attr( AFFECTS_T ) >> '(' >> lineRef >> ',' >> lineRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';
		AffectsBip %= no_case[ lit( "AffectsBip" ) ] >> attr( AFFECTSBIP ) >> '(' >> lineRef >> ',' >> lineRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';
		AffectsBipT %= no_case[ lit( "AffectsBip*" ) ] >> attr( AFFECTSBIP_T ) >> '(' >> lineRef >> ',' >> lineRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';

		Sibling %= no_case[ lit( "Sibling" ) ] >> attr( SIBLING ) >> '(' >> nodeRef >> ',' >> nodeRef [ push_back( at_c<1>( _val ), _1 ) ] >> ')';

		patternCond %= pattern >> *( lit( "and" ) >> pattern );
		pattern %= hold[ if_ ] | hold[ assign ] | while_;
		assign %= attr( PATTERN ) >> entity >> '(' >> varRef [ push_back( at_c<1>( _val ), _1 ) ] >> ',' >> ( expression_spec [ push_back( at_c<1>( _val ), _1 ) ] | any [ push_back( at_c<1>( _val ), _1 ) ] ) >> ')';
		// using lit to avoid errors
		expression_spec %= ( ( lit( '_' ) >> lit( '"' ) >> expr >> lit( '"' ) >> lit( '_' ) ) >> attr( SUBEXPRESSION ) ) | ( ( '"' >> expr >> '"' ) >> attr( EXPRESSION ) );
		expr %= hold[ ( term >> ( char_( '+' ) | char_( '-' ) ) >> expr ) ] | term;
		term %= hold[ ( factor >> char_( '*' ) >> term ) ] | factor;
		factor %= synonym | ( char_( '0' ) | INTEGER ) | ( char_( '(' ) >> expr >> char_( ')' ) );
		
		if_ %= attr( PATTERN_I ) >> entity >> '(' >> varRef [ push_back( at_c<1>( _val ), _1 ) ] >> ',' >> any [ push_back( at_c<1>( _val ), _1 ) ] >> ',' >> any [ push_back( at_c<1>( _val ), _1 ) ] >> ')';
		while_ %= attr( PATTERN ) >> entity >> '(' >> varRef [ push_back( at_c<1>( _val ), _1 ) ] >> ',' >> any [ push_back( at_c<1>( _val ), _1 ) ] >> ')';

		start %= eps >> *declaration >> select_cl;
	}

};


QueryParser::QueryParser()
{

}

QueryParser::~QueryParser()
{

}

bool QueryParser::Parse( std::string query, query_tree& tree )
{
	pql_query pql;
	pql_grammar<std::string::const_iterator> grammar;
	std::string::const_iterator iter = query.begin();
	std::string::const_iterator end = query.end();
	bool r = phrase_parse( iter, end, grammar, space, pql );

	if ( r && iter == end )
	{
		std::for_each( pql.variables.begin(), pql.variables.end(), [ &tree ] ( pql_declaration declaration )
		{
			query_declaration qd;
			qd.type = declaration.type;

			for ( auto name = declaration.names.begin(); name != declaration.names.end(); name++ )
			{
				qd.name = *name;
				tree.variables.push_back( qd );
			}
		});

		tree.targets = pql.select.targets;

		std::for_each( pql.select.relations.begin(), pql.select.relations.end(), [ &tree ] ( std::vector<query_relation> relations )
		{
			tree.relations.insert( tree.relations.end(), relations.begin(), relations.end() );
		});
	}

	return ( r && iter == end );
}